// MyFamily - XML based genealogy program.
// Copyright (C) 2006  Doug Swisher
// See the file COPYING.txt for license details.

using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;


namespace DataModel
{

    /// <summary>
    /// This class is intended to represent the names of individuals within a genealogy.
    /// </summary>
    /// <remarks>
    /// The class is based heavily on the GEDCOM 5.5 PERSONAL_NAME_STRUCTURE, without the
    /// source citation and notes.
    /// </remarks>
    public class PersonalName : IComparable
    {
        private string givenNames;
        private string surname;


        public PersonalName(string givenNames, string surname)
        {
            this.givenNames = givenNames;
            this.surname = surname;
        }


        private static PersonalName unknown = new PersonalName("?", "?");

        public static PersonalName Unknown
        {
            get { return unknown; }
        }


        public static PersonalName Parse(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }

            name = name.Trim();


            string[] parts = name.Split('/');

            if (parts.Length == 0)
            {
                // Huh?  How could this happen?
                return null;
            }
            else if (parts.Length == 1)
            {
                if (name.Contains("/"))
                {
                    return new PersonalName(null, parts[0]);
                }
                else
                {
                    return new PersonalName(parts[0], null);
                }
            }
            else if (parts.Length == 2)
            {
                return new PersonalName(parts[0].Trim(), parts[1].Trim());
            }
            else
            {
                if (string.IsNullOrEmpty(parts[0]))
                {
                    return new PersonalName(null, parts[1].Trim());
                }
                else
                {
                    return new PersonalName(parts[0].Trim(), parts[1].Trim());
                }
            }
        }


        public string ToString(string format)
        {
            switch (format)
            {
                case "N":
                default:
                    return this.givenNames + " " + this.surname;

                case "S":
                    return this.surname + ", " + this.givenNames;

                case "/":
                    return this.givenNames + " /" + this.surname + "/";
            }
        }


        public override string ToString()
        {
            return this.ToString("N");
        }


        public string GivenNames
        {
            get { return this.givenNames; }
            set { this.givenNames = value; }
        }


        public string Surname
        {
            get { return this.surname; }
            set { this.surname = value; }
        }


        #region IComparable Members

        public int CompareTo(object obj)
        {
            // TODO - Doug - 8/3/2006 - Make this a little bit smarter?

            if (obj == null)
            {
                return 1;
            }

            PersonalName other = obj as PersonalName;

            if (other == null)
            {
                return 1;
            }

            return this.ToString("S").CompareTo(other.ToString("S"));
        }

        #endregion



        public override bool Equals(object obj)
        {
            // Simple cases...
            if (obj == null)
            {
                return false;
            }

            PersonalName other = obj as PersonalName;

            if (other == null)
            {
                return false;
            }

            // Compare members...
            if (!string.Equals(this.givenNames, other.givenNames))
            {
                return false;
            }

            if (!string.Equals(this.surname, other.surname))
            {
                return false;
            }

            return true;
        }


        public override int GetHashCode()
        {
            // TODO - Doug - 8/27/2006 - do we want to change this implementation at all?
            return base.GetHashCode();
        }
    }



    [TestFixture]
    public class PersonalNameTests
    {
        [Test]
        public void CompareTo()
        {
            PersonalName name = new PersonalName("A B", "D");

            Assert.AreEqual(1, name.CompareTo(null), "Everything is greater than null.");
            Assert.AreEqual(0, name.CompareTo(new PersonalName("A B", "D")), "Matching names are equal.");
            Assert.AreEqual(1, name.CompareTo(new PersonalName("A B", "C")), "C comes before D.");
        }



        [Test]
        public void TestOutput()
        {
            PersonalName name = new PersonalName("First Middle", "Last");

            Assert.AreEqual("First Middle Last", name.ToString(), "ToString() mismatch");
            Assert.AreEqual("First Middle Last", name.ToString("N"), "ToString(N) mismatch");
            Assert.AreEqual("Last, First Middle", name.ToString("S"), "ToString(S) mismatch");
            Assert.AreEqual("First Middle /Last/", name.ToString("/"), "ToString(/) mismatch");
        }


        private void DoParseTest(string fullName, string expectedGiven, string expectedSurname)
        {
            PersonalName name = PersonalName.Parse(fullName);
            Assert.IsNotNull(name, "Parse result should not be null.");
            Assert.AreEqual(expectedGiven, name.GivenNames, "Given name mismatch");
            Assert.AreEqual(expectedSurname, name.Surname, "Surname mismatch");
        }


        [Test]
        public void ParseThreePartSlash()
        {
            this.DoParseTest("First Middle /Last/", "First Middle", "Last");
        }


        [Test]
        public void ParseTwoPartSlash()
        {
            this.DoParseTest("First /Last/", "First", "Last");
        }


        [Test]
        public void ParseOnePartSlash()
        {
            this.DoParseTest("/Last/", null, "Last");
        }


        [Test]
        public void ParseOnePartNoSlash()
        {
            this.DoParseTest("First", "First", null);
        }


        [Test]
        public void ParseOnePartNoSlashWithSpace()
        {
            this.DoParseTest("First ", "First", null);
        }


        [Test]
        public void ParseTwoPartNoSlash()
        {
            this.DoParseTest("First Middle", "First Middle", null);
        }


        [Test]
        public void ParseSpaceInLastName()
        {
            this.DoParseTest("First Middle /Last1 Last2/", "First Middle", "Last1 Last2");
        }


        [Test]
        public void ParseNoSpaceBeforeLastName()
        {
            this.DoParseTest("Robert Eugene/Williams/", "Robert Eugene", "Williams");
        }


        [Test]
        public void ParseExtraMiddleName()
        {
            this.DoParseTest("George Herbert Walker /Bush/", "George Herbert Walker", "Bush");
        }
    }
}
